console.log('8.1 类与对象编程');
// 8.1.1 创建对象
// 创建对象，增加属性和方法
// let person = new Object();
// person.name = "Nicholas";
// person.age = 29;
// person.job = "Soft Engineer";
// person.sayName = function () {
//   console.log(this.name);
// }
// person.sayName();
// // 面向字变量写法
// let person2 = {
//   name : "Nicholas",
//   age : 29,
//   job: "Software Engineer",
//   sayName() {
//     console.log(this.name+"2");
//   }
// }
// person2.sayName();

// 数据属性

// 8.1.1 属性的类型
// 数据属性
// var person3 = {}
// 想修改属性的默认特性，要使用defineProperty() 方法
/**
 * defineProperty()  arg1 ： 要添加属性的对象，arg2: 具体的名称 arg3: 一个描述的对象
*/
// Object.defineProperty(person3,'name',{
//   　　configurable:false,
//   　　value:'李四'
// });

// person3.name = '王五';  
// console.log(person3.name);
// person3.name = "王五";
// console.log(person3.name);
// console.log(person3.name);　　//李四
// 
// for(var key in person3){
//  　　console.log("person3的属性" + key + "=" + person3[key]);　　//person的属性name=李四
// }　
// 访问属性
// 伪私有成员 year_, 公共成员 edition
// let book = {
//   year_: 2017,
//   edition: 1
// };

// Object.defineProperty(book, "year", {
//   get () {
//     return this.year_;
//   },
//   set (newValue) {
//     if (newValue > 2017) {
//       this.year_ = newValue;
//       this.edition += newValue - 2017;
//     }
//   }
// });
// book.year = 2018;
// console.log(book.edition);


// 8.1.2 定义多个属性
// let book = {};
// Object.defineProperties(book,{
//   year_: {
//     // 通过 Object.defineProperties() 创建的属性的值默认是不能被修改的，想修改writable = true
//     writable: true,
//     value: 2017
//   },
//   edition: {
//     writable: true,
//     value: 1
//   },
//   year: {
//   get () {
//     return this.year_;
//   },
//   set (newValue) {
//     if (newValue > 2017) {
//       this.year_ = newValue;
//       this.edition += newValue - 2017;
//     }
//   }
//   }
// });
// book.year = 2018;
// console.log(book.year_);
// console.log(book.edition);

// 8.1.3 获得对象的属性特性
// let descriptor = Object.getOwnPropertyDescriptor(book, "year_");
// console.log(descriptor.value); // 2017
// console.log(descriptor.configurable);// false
// console.log(typeof descriptor.get);// undefined
// let descriptor2 = Object.getOwnPropertyDescriptor(book, "year");

// console.log(descriptor2.value); // undefined
// console.log(descriptor2.enumerable); // false
// console.log(typeof descriptor2.get);// function

// console.log(Object.getOwnPropertyDescriptors(book))


// 8.1.4 合并对象 assign

// let dest, src, result;
/**
 *  简单复制
 */
// dest = {};
// src = { id: 'src' };

// result = Object.assign(dest, src)
// // assign 修改目标对象，也会返回修改后的目标对象

// console.log(dest === result); // true
// console.log(dest !== src); // true 
// console.log(result);// { "id": "src" }
// console.log(dest);// { "id": "src" }
/**
 *  多个源对象
 */
// dest = {};
// result = Object.assign(dest, { a: 'foo' }, { b: 'bar' });
// console.log(result); // { "a": "foo", "b": "bar" }

/**
  * 获取函数与设置函数
 */
// dest = {
//   set a (val) {
//     console.log('invoked dest setter with param ${val}')
//   }
// };
// src = {
//   get a () {
//     console.log('Invoked src getter');
//     return 'foo';
//   }
// };

// Object.assign(dest, src);
// 调用src 的get
// 调用dest 的 set 方法 传入参数 ”foo“
// 因为这里的设置函数并不进行赋值操作，所以并没有把值转移过来

// console.log(dest); // { set a(val) {...}}

/**
 * 覆盖属性
*/

// dest = { id: 'dest' };
// result = Object.assign(dest, { id: 'src1', a: 'foo' }, { id: 'src2', a: 'bar' });
// // Object.assign() 会覆盖属性
// console.log(result); // { "id": "thrid", "a": "bar" }
// // 可以通过 目标对象上的 set() 观察覆盖过程
// dest = {
//   set id (x) {
//     console.log(x);
//   }
// }
// Object.assign(dest, { id: 'first' }, { id: 'second' }, { id: 'thrid' });
// first
// second
// thrid

// 对象引用

// dest = {};
// src = { a: 'foo' };
// Object.assign(dest, src);
// // 浅复制意味着只复制对象的引用
// console.log(dest);// { "a": "foo" }
// console.log(dest.a === src.a);// true

/**
 * 错误处理
 */
// let dest , src , result;
// dest = {};
// src = {
//   a: 'foo',
//   get b () {
//     // Object.assign() 在调用这个函数时会抛出错误
//     throw new Error();
//   },
//   c: 'bar'
// };
// try {
//   Object.assign(dest, src);
// } catch (e) { }
// Object.assign() 没法回滚已完成的修改
// 修改到b() 的时候抛出错误，之前属性a已经复制了，会继续存在。
// console.log(dest); // { "a": "foo" }

// 8.1.5

// console.log(Object.is(true, 1)); // false  
// console.log(Object.is({}, {})); // false  
// console.log(Object.is("2", 2)); // false  
// // 正确的 0、-0、+0 相等/不等判定 
// console.log(Object.is(+0, -0)); // false  
// console.log(Object.is(+0, 0)); // true  
// console.log(Object.is(-0, 0)); // false  
// // 正确的 NaN 相等判定 
// console.log(Object.is(NaN, NaN)); // true  

// 比较多个


// function recursivelyCheckEqual(x, ...rest) {  
//  return Object.is(x, rest[0]) &&  
//  (rest.length < 2 || recursivelyCheckEqual(...rest));  
// }

// const a = [1, 1, 1];
// const b = [1, 1, 2];
// let result = recursivelyCheckEqual(...a);
// // true
// let result2 = recursivelyCheckEqual(...b);
// // false
// console.log(result);
// console.log(result2);
// 8.1.6
// let name = "mtjj";

// let person = {
//   name
// }
// console.log(person);

// 可计算属性
// const nameKey = 'name';
// const ageKey = 'age';
// const jobKey = 'job';
// let uniqueToken = 0;
// function getUniqueKey (key) {
//   return `${key}_${uniqueToken++}`;
// }

// let person = {
// 	[getUniqueKey(nameKey)] : "Matt",
//   [getUniqueKey(ageKey)]: "27",
//   [getUniqueKey(jobKey)]: "job"
// };
// console.log(person);//{name: "Matt", age: "27"}

//简写方法名

// let person = {
//   name_ : '',
//   set name (name) {
//      this.name_ = name;
//   },
//   get name () {
//     return this.name_;
//   },
//   sayName () {
//     console.log(`My name is ${this.name_}`);
//   }
// };
// person.name = "李四";
// person.sayName(); // My name is 李四




// const methodKey = 'sayName';
// let person = {
//   [methodKey](name) {
//     console.log(`My name is ${name}`);
//   }
// }
// person.sayName('Matt'); // My name is Matt


// 解构
// 不使用解构
// let person = {
//   name: 'Matt',
//   age: 27
// }
// let personName = person.name;
// let personAge = person.age;
// console.log(personName);
// console.log(personAge);

// 使用解构

// let { name:personName, age:personAge } = person;

// console.log(personName);
// console.log(personAge);

// let { name , job = 'Software engineer'} = person;

// console.log(name);// Matt
// console.log(job); // Software engineer
/**
 * 解构在内部使用函数ToObject() （不能在运行时环境中直接访问）把源数据结构转换为对象。这意味着在对象解构的上下文中，原始值会被当成对象。这也意味着（根据ToObject() 的定义），null 和
undefined 不能被解构，否则会抛出错误。
*/
// let { length } = 'foobar';
// console.log(length);
// let { constructor: c } = 4;
// console.log(c === Number);  // true
// let { _ } = null;           // TypeError
// let { _ } = undefined;      // TypeError
// let personName , personAge;
// let person = {
// 	name: 'Matt',
//   age: 27 
// };
// ({name: personName, age: personAge} = person);
// console.log(personName,personAge);
// 嵌套解构
// let person = {
//   name: 'Matt',
//   age: 27,
//   job: {
//     title: 'Software engineer'
//   }
// };
// let personCopy = {};
// ({
//   name: personCopy.name,
//   age: personCopy.age,
//   job: personCopy.job
// } = person);
// // 因为一个对象的引用被赋值给personCopy，所以修改
// // person.job对象的属性也会影响personCopy
// person.job.title = 'Hacker';
// console.log(personCopy);// { "name": "Matt", "age": 27, "job": { "title": "Hacker" } }

// let person = {
//   job: {
//     title: 'Software engineer'
//   }
// };
// let personCopy = {};
// // foo 在源对象上是 undefined

// ({
//   foo: {
//     bar: personCopy.bar
//   }
// } = person); // Uncaught TypeError: Cannot read properties of undefined (reading 'bar')

// // job 在目标对象上是 undefined
// ({
//   job: {
//     title: personCopy.job.title //Uncaught TypeError: Cannot set properties of undefined (setting 'title')

//   }
// } = person)

// 部分解构
// let person = {
//   name: 'Matt',
//   age: 27
// };
// let personName , personBar , personAge;
// try{
//   // person.foo 是undefined 所以会抛出错误
//  ({ name: personName, foo: { bar: personBar}, age: personAge } = person);
// }catch(e) {}

// console.log(personName, personBar, personAge);
// // Matt , undefined, undefined


// 参数匹配上下文
let person = {
  name: 'Matt',
  age: 27
};

function printPerson (foo, { name, age }, bar) {
  console.log(arguments);
  console.log(name,age);
}

function printPerson2 (foo, { name: personName, age: personAge }, bar) {
  console.log(arguments);
  console.log(personName,personAge);
}

printPerson('1st', person, '2nd');
// { "0": "1st", "1": { "name": "Matt", "age": 27 }, "2": "2nd" 
// Matt 27

printPerson2('1st', person, '2nd');
// { "0": "1st", "1": { "name": "Matt", "age": 27 }, "2": "2nd" }
// Matt 27
